

#include <rtc_base/logging.h>
#include <rtc_base/time_utils.h>
#include "ice/ice_transport_channel.h"
#include "ice/udp_port.h"
#include "ice/ice_connection.h"
namespace xrtc
{
    const int PING_INTERVAL_DIFF = 5;
    void IcePingCb(EventLoop * /*el*/, TimerWatcher * /*w*/, void *data)
    {
        IceTransportChannel *channel = (IceTransportChannel *)data;
        channel->OnCheckAndPing();
    }

    IceTransportChannel::IceTransportChannel(EventLoop *el, PortAllocator *alloctor, const std::string &transport_name_, IceCandidateComponent component)
        : el_(el), transport_name_(transport_name_), component_(component), allocator_(alloctor), ice_controller_(new IceController(this))
    {
        RTC_LOG(LS_INFO) << "ice transport channel created, transport_name: " << transport_name_
                         << ",component: " << component_;
        ping_watcher_ = el_->CreateTimer(IcePingCb, this, true);
    }
    IceTransportChannel::~IceTransportChannel()
    {
        if (ping_watcher_)
        {
            el_->DeleteTimer(ping_watcher_);
            ping_watcher_ = nullptr;
        }
        std::vector<IceConnection *> connections = ice_controller_->Connections();
        for (auto conn : connections)
        {
            conn->Destory();
        }

        for (auto port : ports_)
        {
            delete port;
        }
        ports_.clear();
        ice_controller_.reset(nullptr);
        RTC_LOG(LS_INFO) << ToString() << ": IceTransportChannel destory.";
    }
    int IceTransportChannel::SendPacket(const char *data, size_t data_len)
    {
        if (!ice_controller_->ReadyToSend(selected_connection_))
        {
            RTC_LOG(LS_WARNING) << ToString() << ": Selected connection not ready to send";
            return -1;
        }
        int sent = selected_connection_->SendPacket(data, data_len);
        if (sent <= 0)
        {
            RTC_LOG(LS_WARNING) << ToString() << ": Selected connection send failed";
        }
        return sent;
    }

    void IceTransportChannel::SetIceParams(const IceParameters &ice_params)
    {

        ice_params_ = ice_params;
        RTC_LOG(LS_INFO) << "set ice params ICE params"
                         << "transport_name: " << transport_name_
                         << ", component :" << component_
                         << ", ufrag : " << ice_params_.ice_ufrag
                         << ", pwd : " << ice_params_.ice_pwd;
    }
    void IceTransportChannel::SetRemoteIceParams(const IceParameters &ice_params)
    {
        remote_ice_params_ = ice_params;
        RTC_LOG(LS_INFO) << "set ice params ICE params"
                         << "transport_name: " << transport_name_
                         << ", component :" << component_
                         << ", ufrag : " << remote_ice_params_.ice_ufrag
                         << ", pwd : " << remote_ice_params_.ice_pwd;

        for (auto conn : ice_controller_->Connections())
        {
            conn->MaybeSetRemoteIceParams(ice_params);
        }
        SortConnectionAndUpdateState();
    }

    void IceTransportChannel::GatheringCandidate()
    {
        if (ice_params_.ice_ufrag.empty() || ice_params_.ice_pwd.empty())
        {

            RTC_LOG(LS_WARNING) << "connot gathering candidate becase ICE param is empty"
                                << ", transport_name: " << transport_name_
                                << ", component :" << component_
                                << ", ufrag : " << ice_params_.ice_ufrag
                                << ", pwd : " << ice_params_.ice_pwd;
            return;
        }
        auto network_list = allocator_->GetNetworks();
        if (network_list.empty())
        {
            RTC_LOG(LS_WARNING) << "connot gathering candidate becase network_list is empty"
                                << ", transport_name: " << transport_name_
                                << ", component :" << component_;
            return;
        }

        for (auto network : network_list)
        {
            UDPPort *port = new UDPPort(el_, transport_name_, component_, ice_params_);

            port->signal_unknown_address.connect(this, &IceTransportChannel::OnUnknownAddress);

            Candidate c;
            ports_.push_back(port);

            int ret = port->CreateIceCandidate(network, allocator_->MinPort(), allocator_->MaxPort(), c);
            if (ret != 0)
            {
                continue;
            }
            local_candidates_.push_back(c);
        }
        // 收集完成candidat 之后发送信号给上层，candidate 收集完成
        SignalCandidateAllocateDone(this, local_candidates_);
    }

    void IceTransportChannel::OnUnknownAddress(UDPPort *port, const rtc::SocketAddress &addr, StunMessage *msg, const std::string &remote_ufrag)
    {

        const StunUInt32Attribute *priority_attr = msg->GetUint32(STUN_ATTR_PRIORITY);
        if (!priority_attr)
        {
            RTC_LOG(LS_WARNING) << ToString() << ": priority not found in the binding request messsage ,remote_addr :" << addr.ToString();
            // 发送错误响应
            port->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST);
            return;
        }
        uint32_t remote_priority = priority_attr->Value();

        // RTC_LOG(LS_INFO) << "====remote_priority:" << remote_priority;
        // 根据反射的地址创建candidate 用于以后通信
        Candidate remote_candidate;
        remote_candidate.component = component_;
        remote_candidate.protocol = "udp";
        remote_candidate.address = addr;
        remote_candidate.username = remote_ufrag;
        remote_candidate.password = remote_ice_params_.ice_pwd;
        remote_candidate.priority = remote_priority;
        remote_candidate.type = PRFLX_PORT_TYPE;

        RTC_LOG(LS_INFO) << ToString() << "   create peer reflexvie candidata"
                         << remote_candidate.ToString();
        IceConnection *conn = port->CreateConnection(remote_candidate);
        if (!conn)
        {
            RTC_LOG(LS_WARNING) << "create conenction from peer reflexive candidate error,remote_addr:" << addr.ToString();
            port->SendBindingErrorResponse(msg, addr, STUN_ERROR_SERVER_ERROR, STUN_ERROR_REASON_SERVER_ERROR);
            return;
        }
        RTC_LOG(LS_INFO) << "create conenction from peer reflexive candidate success,remote_addr:" << addr.ToString();

        AddConnection(conn);
        conn->HandleStunBindingRequest(msg);

        // 尝试启动联通行检查
        SortConnectionAndUpdateState();
    }

    void IceTransportChannel::SortConnectionAndUpdateState()
    {

        MaybeSwitchSelectedConnection(ice_controller_->SortAndSwitchConnection());
        UpdateState();
        MaybeStartPinging();
    }
    void IceTransportChannel::MaybeSwitchSelectedConnection(IceConnection *conn)
    {

        if (!conn) // no switch
        {
            return;
        }
        SwitchSelectedConnection(conn);
    }

    void IceTransportChannel::SwitchSelectedConnection(IceConnection *conn)
    {
        // RTC_LOG(LS_INFO) <<"Switch selected  connection:" <<conn->ToString();
        IceConnection *old_selected_conntion = selected_connection_;
        selected_connection_ = conn;
        if (old_selected_conntion)
        {

            old_selected_conntion->SetSelected(false);
            RTC_LOG(LS_INFO) << ToString() << ": previous connection : " << old_selected_conntion->ToString();
        }
        if (selected_connection_)
        {
            RTC_LOG(LS_INFO) << ToString() << ": New slected connection : " << conn->ToString();
            selected_connection_ = conn;
            selected_connection_->SetSelected(true);
            ice_controller_->SetSelectedConnection(selected_connection_);
        }
        else
        {

            RTC_LOG(LS_INFO) << ToString() << " : No connection selected";
        }
    }
    void IceTransportChannel::MaybeStartPinging()
    {
        if (start_pinging_)
        {
            return;
        }
        if (ice_controller_->HasPingableConnection())
        {

            RTC_LOG(LS_INFO) << ToString() << ":Have a pingable connection  for the first time, starting to ping";
            // 启动定时器
            el_->StartTimer(ping_watcher_, cur_ping_interval_ * 1000);
            start_pinging_ = true;
        }
    }
    std::string IceTransportChannel::ToString()
    {
        std::stringstream ss;
        ss << "Channel[" << this << ":" << transport_name_ << ":" << component_ << "]";
        return ss.str();
    }

    void IceTransportChannel::AddConnection(IceConnection *conn)
    {
        conn->SignalStateChange.connect(this, &IceTransportChannel::OnConnectionStateChange);
        conn->SignalConnectionDestory.connect(this, &IceTransportChannel::OnConnectionDestoryed);
        conn->SignalReadPacket.connect(this, &IceTransportChannel::OnReadPacket);
        _had_connection = true;
        ice_controller_->AddConnection(conn);
    }

    void IceTransportChannel::OnReadPacket(IceConnection * /*conn*/, const char *buf, size_t len, int64_t ts)
    {

        SignalReadPacket(this, buf, len, ts);
    }
    void IceTransportChannel::OnConnectionDestoryed(IceConnection *conn)
    {
        ice_controller_->OnConnectionDestroyed(conn);
        RTC_LOG(LS_INFO) << ToString() << ": Remove connection :" << conn
                         << " with " << ice_controller_->Connections().size() << " remaining";
        if (selected_connection_ == conn)
        {
            RTC_LOG(LS_INFO) << ToString() << ": Selected connection destroyed ,should select a new connection";
            SwitchSelectedConnection(nullptr);
            SortConnectionAndUpdateState();
        }
        else
        {

            UpdateState();
        }
    }
    void IceTransportChannel::OnConnectionStateChange(IceConnection * /*conn*/)
    {
        SortConnectionAndUpdateState();
    }
    // 对客户端探测是否在线机制
    void IceTransportChannel::UpdateConnectionStates()
    {
        std::vector<IceConnection *> connections = ice_controller_->Connections();
        int now = rtc::TimeMillis();
        for (auto conn : connections)
        {
            conn->UpdateState(now);
        }
    }
    void IceTransportChannel::OnCheckAndPing()
    {
        UpdateConnectionStates();
        auto result = ice_controller_->SelectConnectionToPing(last_ping_sent_ms_ - PING_INTERVAL_DIFF);
        // RTC_LOG(LS_WARNING) << "====conn: " << result.conn << " ping interval: " << result.ping_interval;

        if (result.conn)
        {
            IceConnection *conn = (IceConnection *)result.conn;
            PingConnection((IceConnection *)result.conn);
            ice_controller_->MarkConnectionPinged(conn);
        }

        if (cur_ping_interval_ != result.ping_interval)
        {
            cur_ping_interval_ = result.ping_interval;
            el_->StopTimer(ping_watcher_);
            el_->StartTimer(ping_watcher_, cur_ping_interval_ * 1000);
        }
    }

    void IceTransportChannel::PingConnection(IceConnection *conn)
    {

        last_ping_sent_ms_ = rtc::TimeMillis();
        conn->Ping(last_ping_sent_ms_);
    }
    void IceTransportChannel::UpdateState()
    {
        bool writable = selected_connection_ && selected_connection_->Writable();
        SetWritable(writable);
        bool receiving = false;
        for (auto conn : ice_controller_->Connections())
        {

            if (conn->Receiving())
            {
                receiving = true;
            }
        }
        SetReceiving(receiving);

        IceTransportState state = ComputeIceTransportState();
        if (state != state_)
        {
            state_ = state;
            SignalIceStateChanged(this);
        }
    }
    IceTransportState IceTransportChannel::ComputeIceTransportState()
    {
        bool has_connection = false;
        for (auto conn : ice_controller_->Connections())
        {
            if (conn->Active())
            {
                has_connection = true;
                break;
            }
        }
        if (_had_connection && !has_connection)
        {
            return IceTransportState::kFailed;
        }
        if (_has_been_connection && !Writable())
        {
            return IceTransportState::kDisconnected;
        }
        if (!_had_connection && !has_connection)
        {
            return IceTransportState::kNew;
        }
        if (has_connection && !Writable())
        {
            return IceTransportState::kChecking;
        }
        return IceTransportState::kConnected;
    }
    void IceTransportChannel::SetWritable(bool writable)
    {
        if (writable_ == writable)
        {
            return;
        }
        if (writable)
        {
            _has_been_connection = true;
        }
        writable_ = writable;
        RTC_LOG(LS_INFO)
            << ToString() << ": Change writable to " << writable_;
        SignalWritableState(this);
    }
    void IceTransportChannel::SetReceiving(bool receiving)
    {
        if (receiving_ == receiving)
        {
            return;
        }
        receiving_ = receiving;
        RTC_LOG(LS_INFO)
            << ToString() << ": Change receving to " << receiving_;
        SignalReceivingState(this);
    }

} // namespace xrtc
